package com.atguigu.gmall.product.service.impl;

import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.product.biz.CategoryBizSerivce;
import com.atguigu.gmall.product.entity.*;
import com.atguigu.gmall.product.entity.entity.CategroyViewEntity;
import com.atguigu.gmall.product.mapper.BaseTrademarkMapper;
import com.atguigu.gmall.product.service.SkuAttrValueService;
import com.atguigu.gmall.product.service.SkuImageService;
import com.atguigu.gmall.product.service.SkuSaleAttrValueService;
import com.atguigu.gmall.product.vo.SkuInfoSaveVo;
import com.atguigu.gmall.product.vo.SkuInfoUpdateVo;
import com.atguigu.gmall.search.entity.Goods;
import com.atguigu.gmall.search.entity.SearchAttr;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.atguigu.gmall.product.service.SkuInfoService;
import com.atguigu.gmall.product.mapper.SkuInfoMapper;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import com.atguigu.gmall.feign.search.SearchFeignClient;
import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 * @author lzw
 * @description 针对表【sku_info(库存单元表)】的数据库操作Service实现
 * @createDate 2024-10-15 20:39:59
 */
@Slf4j
@Service
public class SkuInfoServiceImpl extends ServiceImpl<SkuInfoMapper, SkuInfo> implements SkuInfoService {

    @Autowired
    SkuInfoMapper skuInfoMapper;

    @Autowired
    SkuImageService skuImageService;

    @Autowired
    SkuAttrValueService skuAttrValueService;

    @Autowired
    SkuSaleAttrValueService saleAttrValueService;

    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    BaseTrademarkMapper baseTrademarkMapper;

    @Autowired
    CategoryBizSerivce categoryBizSerivce;

    @Autowired
    RedissonClient redisson;

    @Autowired
    ThreadPoolExecutor executor;

    @Autowired
    SearchFeignClient  searchFeignClient;

    //这个池子的队列是Integer.MAX；容易OOM
    ScheduledExecutorService pool = Executors.newScheduledThreadPool(4);

    @PostConstruct
    public void initSkuIdBloom() {
        // 初始化布隆过滤器
        RBloomFilter<Long> filter = redisson.getBloomFilter(RedisConst.BLOOM_SKUID);
        if (!filter.isExists()) {
            filter.tryInit(1000000, 0.000001);
            List<Long> allSkuId = getAllSkuId();
            allSkuId.stream().forEach(id -> filter.add(id));
        }
    }

    @Override
    public void saveSkuInfo(SkuInfoSaveVo vo) {
        //1、保存sku_info
        SkuInfo skuInfo = new SkuInfo();
        BeanUtils.copyProperties(vo, skuInfo);
        skuInfoMapper.insert(skuInfo);

        Long skuId = skuInfo.getId();


        //2、保存sku_images
        List<SkuImage> images = vo.getSkuImageList().stream()
                .map(item -> {
                    SkuImage image = new SkuImage();
                    BeanUtils.copyProperties(item, image);
                    //回填sku_id
                    image.setSkuId(skuId);
                    return image;
                }).collect(Collectors.toList());
        skuImageService.saveBatch(images);

        //3、保存 sku平台属性名和值
        List<SkuAttrValue> attrValues = vo.getSkuAttrValueList().stream()
                .map(item -> {
                    SkuAttrValue skuAttrValue = new SkuAttrValue();
                    BeanUtils.copyProperties(item, skuAttrValue);
                    //回填sku_id
                    skuAttrValue.setSkuId(skuId);
                    return skuAttrValue;
                }).collect(Collectors.toList());
        skuAttrValueService.saveBatch(attrValues);


        //4、保存  sku销售属性名和值
        List<SkuSaleAttrValue> saleAttrValues = vo.getSkuSaleAttrValueList()
                .stream()
                .map(item -> {
                    SkuSaleAttrValue value = new SkuSaleAttrValue();
                    BeanUtils.copyProperties(item, value);
                    //回填sku_id、spu_id
                    value.setSkuId(skuId);
                    value.setSpuId(skuInfo.getSpuId());
                    return value;
                })
                .collect(Collectors.toList());

        saleAttrValueService.saveBatch(saleAttrValues);


        //把商品信息添加到布隆过滤器
        RBloomFilter<Object> filter = redisson.getBloomFilter(RedisConst.BLOOM_SKUID);
        filter.add(skuId);
        log.info("sku信息保存成功： skuId={}", skuId);

        //把sku_price 再给缓存存一份
//        redisTemplate.opsForValue().set(RedisConst.SKU_PRICE+skuId,skuInfo.getPrice().toString());
    }

    @Override
    public void updateSkuInfo(SkuInfoUpdateVo vo) {
        //1、去数据库修改

        //2、延迟双删
        //2.1）、立即删
        redisTemplate.delete(RedisConst.SKU_DETAIL_CACHE_PREFIX + vo.getId());
        //2.2）、延迟10s删。
        //自己 new Thread() 会有OOM风险。无限请求不停new就会 oom
        //        new Thread(()->{
        //            Thread.sleep(10000);
        //            redisTemplate.delete(RedisConst.SKU_DETAIL_CACHE_PREFIX+vo.getId());
        //        }).start();


        //提交延迟任务。有OOM风险
        pool.schedule(() -> {
            redisTemplate.delete(RedisConst.SKU_DETAIL_CACHE_PREFIX + vo.getId());
        }, 10, TimeUnit.SECONDS);


        //立马结束.
    }

    @Override
    public void onSale(Long skuId) {
        //1、修改状态
        this.changeOnSale(skuId,1);
        //2、 把sku保存到es中  http://sph.atguigu.cn/
        Goods goods = prepareGoods(skuId);
        //远程调用ES服务进行保存
        searchFeignClient.onSaleGoods(goods);
    }

    @Override
    public void changeOnSale(Long skuId, int status) {
        //1、修改数据库状态
        skuInfoMapper.updateSaleStatus(skuId, status);
        //TODO 2、上架的商品代表可以被检索到
    }

    @Override
    public void cancelSale(Long skuId) {
        this.changeOnSale(skuId,0);

        //删除es中这个商品
        searchFeignClient.cancelSale(skuId);
    }

    @Override
    public BigDecimal getSkuPrice(Long skuId) {
        return skuInfoMapper.getSkuPrice(skuId);
    }

    @Override
    public List<Long> getAllSkuId() {
        return skuInfoMapper.getAllSkuId();
    }

    /**
     * 生成某个商品在es中进行存储的数据模型
     * @param skuId
     * @return
     */
    private Goods prepareGoods(Long skuId) {
        SkuInfo skuInfo = skuInfoMapper.selectById(skuId);
        //1、查询商品sku的基本信息
        Goods goods = new Goods();
        CompletableFuture<Void> skuInfoFuture = CompletableFuture.runAsync(() -> {
            goods.setId(skuId);
            goods.setDefaultImg(skuInfo.getSkuDefaultImg());
            goods.setTitle(skuInfo.getSkuName());
            goods.setPrice(skuInfo.getPrice());
            goods.setCreateTime(new Date());
        }, executor);


        //2、查询品牌
        CompletableFuture<Void> tradeMarkFuture = CompletableFuture.runAsync(() -> {
            BaseTrademark trademark = baseTrademarkMapper.selectById(skuInfo.getTmId());
            goods.setTmId(skuInfo.getTmId());
            goods.setTmName(trademark.getTmName());
            goods.setTmLogoUrl(trademark.getLogoUrl());
        }, executor);



        //3、查询分类
        CompletableFuture<Void> categoryFuture = CompletableFuture.runAsync(() -> {
            Long c3Id = skuInfo.getCategory3Id();
            CategroyViewEntity view = categoryBizSerivce.getCategoryView(c3Id);
            goods.setCategory1Id(view.getC1id());
            goods.setCategory1Name(view.getC1name());
            goods.setCategory2Id(view.getC2id());
            goods.setCategory2Name(view.getC2name());
            goods.setCategory3Id(view.getC3id());
            goods.setCategory3Name(view.getC3name());
        }, executor);

        //4、热度分，商品每次点击更新es中当前商品热度分
        goods.setHotScore(0L);

        //5、查询某个商品的所有平台属性，名和值
        CompletableFuture<Void> attrFuture = CompletableFuture.runAsync(() -> {
            List<SearchAttr> searchAttrs = skuAttrValueService.getSkuAttrAndValue(skuId);
            goods.setAttrs(searchAttrs);
        }, executor);

        CompletableFuture.allOf(skuInfoFuture,
                tradeMarkFuture,categoryFuture,attrFuture).join();

        return goods;
    }
}




